#ifndef __CORE_PLANE_H__
#define __CORE_PLANE_H__
namespace Core
{
	class rtVector3;
	class rtMatrix3;
	class rtVector4;

	enum PlaneSide
	{
		PLANESIDE_FRONT	 = 0,
		PLANESIDE_BACK	 = 1,
		PLANESIDE_ON	 = 2,
		PLANESIDE_CROSS	 = 3
	};

	enum PlaneType
	{
		PLANETYPE_X			=	0,
		PLANETYPE_Y			=	1,
		PLANETYPE_Z			=	2,
		PLANETYPE_NEGX		=	3,
		PLANETYPE_NEGY		=	4,
		PLANETYPE_NEGZ		=	5,
		PLANETYPE_TRUEAXIAL	=	6,
		PLANETYPE_ZEROX		=	6,
		PLANETYPE_ZEROY		=	7,
		PLANETYPE_ZEROZ		=	8,
		PLANETYPE_NONAXIAL	=	9
	};

	class rtPlane
	{
	public:
		rtPlane(void);
		rtPlane(float a, float b, float c, float d);
		rtPlane(const rtVector3& normal, const float dist);

		static rtPlane plane_origin;

		float			operator[](int index) const;
		float&			operator[](int index);
		rtPlane			operator-() const;						// flips plane
		rtPlane&		operator=(const rtVector3& v);			// sets normal and sets rtPlane::d to zero
		rtPlane			operator+(const rtPlane& p) const;	// add plane equations
		rtPlane			operator-(const rtPlane& p) const;	// subtract plane equations
		rtPlane&		operator*=(const rtMatrix3& m);			// Normal()*= m

		bool			compare(const rtPlane& p) const;						// exact compare, no epsilon
		bool			compare(const rtPlane& p, const float epsilon) const;	// compare with epsilon
		bool			compare(const rtPlane& p, const float normalEps, const float distEps) const;	// compare with epsilon
		bool			operator==(	const rtPlane& p) const;					// exact compare, no epsilon
		bool			operator!=(	const rtPlane& p) const;					// exact compare, no epsilon

		void			zero(void);							// zero plane
		void			setNormal(const rtVector3& normal);		// sets the normal
		const rtVector3& normal(void) const;					// reference to const normal
		rtVector3&		normal(void);							// reference to normal
		float			normalize(bool fixDegenerate = true);	// only normalizes the plane normal, does not adjust d
		bool			fixDegenerateNormal(void);			// fix degenerate normal
		bool			fixDegeneracies(float distEpsilon);	// fix degenerate normal and dist
		float			dist(void) const;						// returns: -d
		void			setDist(const float dist);			// sets: d = -dist
		PlaneType		type(void) const;						// returns plane type

		bool			fromPoints(const rtVector3& p1, const rtVector3& p2, const rtVector3& p3, bool fixDegenerate = true);
		bool			fromVecs(const rtVector3& dir1, const rtVector3& dir2, const rtVector3& p, bool fixDegenerate = true);
		void			fitThroughPoint(const rtVector3& p);	// assumes normal is valid
		bool			heightFit(const rtVector3* points, const int numPoints);
		rtPlane			translate(const rtVector3& translation) const;
		rtPlane&		translateSelf(const rtVector3& translation);
		rtPlane			rotate(const rtVector3& origin, const rtMatrix3& axis) const;
		rtPlane&		rotateSelf(const rtVector3& origin, const rtMatrix3& axis);

		float			distance(const rtVector3& v) const;
		PlaneSide		side(const rtVector3& v, const float epsilon = 0.0f) const;

		bool			lineIntersection(const rtVector3& start, const rtVector3& end) const;
		
		bool			rayIntersection(const rtVector3& start, const rtVector3& dir, float& scale) const;
		bool			planeIntersection(const rtPlane& plane, rtVector3& start, rtVector3& dir) const;

		int				getDimension(void) const;

		const rtVector4& toVec4(void) const;
		rtVector4&		toVec4(void);
		const float*	toFloatPtr(void) const;
		float*			toFloatPtr(void);

	private:
		float			m_A;
		float		    m_B;
		float			m_C;
		float			m_D;
	};
}
#endif


